/**
 * @author Danilo Nishimura
 * @version 0.3c
 * */
/*
Licensed under the MIT License

Copyright (c) 2009 Danilo Nishimura

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

http://www.danilonishimura.com
*/
package com.danilonishimura
{
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.system.System;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import flash.utils.setTimeout;
	
	public class VisualTracer
	{
		
		public static const ALIGN_TOPLEFT:String 		= "tl";
		public static const ALIGN_TOP:String 			= "tp";
		public static const ALIGN_TOPRIGHT:String 		= "tr";
		public static const ALIGN_RIGHT:String			= "rt";
		public static const ALIGN_BOTTOMRIGHT:String	= "br";
		public static const ALIGN_BOTTOM:String			= "bt";
		public static const ALIGN_BOTTOMLEFT:String		= "bl";
		public static const ALIGN_LEFT:String			= "lt";
		
		private static var _container:DisplayObjectContainer = null;
		private static var _traceField:TextField;
		private static var _intDbg:Boolean = false;
		private static var _dbgStr:String = "";
		private static var _visible:Boolean = true;
		private static var _numLines:uint = 45;
		private static var _selectable:Boolean = false;
		private static var _position:String = ALIGN_TOPLEFT;
		private static var _textAlign:String = "left";
		private static var _alignTypes:Array = ["left", "center", "right"];
		private static var _textColor:Number = 0x000000;
		private static var _useHTML:Boolean = false;
		private static var _textBreak:String = "\n";
		private static var _locked:Boolean = false;
		private static var _log:String = "";
		private static var _logButton:Sprite = new Sprite();
		private static var _borderSpacing:Number = 0;
		private static var _header:String = "";
		private static var _warningTimeout:Number = 3;
		
		public static function get warningTimeout():Number
		{
			return _warningTimeout;
		};
		
		public static function set warningTimeout(value:Number):void
		{
			_warningTimeout = value;
		};
		
		public static function set borderSpacing(value:Number):void
		{
			_borderSpacing = value;
			if (checkContainer() && _intDbg)
			{
				debug();
			};
		};
		
		public static function get borderSpacing():Number
		{
			return _borderSpacing;
		};
		
		
		
		private static function createLogButton():void
		{
			if (checkContainer() && _intDbg)
			{
				var myButton:Sprite = new Sprite();
				myButton.mouseChildren = false;
				myButton.buttonMode = true;
				_logButton = myButton;
				var logText:TextField = new TextField();
				logText.autoSize = "left";
				logText.text = "Copy Log";
				logText.backgroundColor = 0xff0000;
				myButton.addChild(logText);
				myButton.addEventListener(MouseEvent.CLICK, copyLog);
				_container.addChild(myButton);
			};
		};
		
		public static function copyToClipboard():Boolean
		{
			if (_log.length > 0)
			{
				flash.system.System.setClipboard(_log);
				ftLog("Log Copied");
				return true;
			}
			else
			{
				ftLog("Nothing to copy");
			};
			return false;
		};
		 
		
		private static function copyLog(e:MouseEvent):void
		{
			copyToClipboard();
		};
		
		private static function ftLog(... rest):void
		{
			debugHeader(" - " + (rest as Array).join(" "));
			setTimeout(clearHeader, warningTimeout * 1000, 1);
		};
		
		public static function set locked(value:Boolean):void
		{
			_locked = value;//debug();
		};
		
		public static function get locked():Boolean
		{
			return _locked;
		};
		
		public static function get log():String
		{
			return _log;
		};
		
		
		public static function init(st_container:DisplayObjectContainer):void
		{
			if(st_container == null)
			{
				throw new Error("An DisplayObjectContainer must be specified");
				return;
			}
			else
			{
				container = st_container;
			};
			createLogButton();
		};
		
		public static function set useHTML(value:Boolean):void
		{
			_useHTML = value;
		};
		
		public static function get useHTML():Boolean
		{
			return _useHTML;
		};
		
		public static function get textColor():Number
		{
			return _textColor;
		};
		
		public static function set textColor(color:Number):void
		{
			_textColor = color;
		};
		
		public static function get textAlign():String
		{
			return _textAlign;
		}
		
		public static function set textAlign(align:String):void
		{
			if (_alignTypes.indexOf(align) >= 0)
			{
				_textAlign = align;
			}
			else
			{
				_textAlign = "left";
			};
			
			if (checkContainer() && _intDbg)
			{
				debug();
			};
			
		};
		
		public static function get position():String{ return _position as String };
		public static function set position(visualDebugString:String):void
		{ 
			_position = visualDebugString;
			if (checkContainer() && _intDbg)
			{
				debug();
			};
		};
		
		public static function get selectable():Boolean
		{
			return _selectable;
		};
		
		public static function set selectable(value:Boolean):void
		{
			_selectable = value;
			if (checkContainer() && _intDbg)
			{
				debug();
			};
		};
		
		public static function get numLines():uint
		{
			return _numLines;
		};
		
		public static function set numLines(value:uint):void
		{
			_numLines = value;
			if (checkContainer() && _intDbg)
			{
				debug();
			};
		};
		
		public static function set visible(value:Boolean):void
		{
			_visible = value;
			if (checkContainer() && _intDbg)
			{
				debug();
			};
		};
		
		public static function get visible():Boolean
		{
			return _visible;
		};
		
		public static function get container():DisplayObjectContainer
		{
			return _container as DisplayObjectContainer;
		};
		public static function set container(container:DisplayObjectContainer):void
		{
			_container = container;
			if (_container.stage)
			{
				addResizeListener(null);
			}
			else
			{
				_container.addEventListener(Event.ADDED_TO_STAGE, addResizeListener);
			};
		};
		
		public function VisualTracer()
		{
			throw new Error("This class is not instantiable. Use static properties and methods.")
		};
		
		private static function resizeDebug(e:Event = null):void
		{
			setPosition();
			debug();
		};
		
		private static function addResizeListener(e:Event):void
		{
			_container.stage.addEventListener(Event.RESIZE, resizeDebug);
			debug();
		};
		
		private static function checkContainer():Boolean
		{
			if (_container == null)
			{
				return false;
			};
			return true;
		};
		
		
		//Debug Methods
		private static function initDebug():void
		{
			if (checkContainer() && !_intDbg)
			{
				_traceField = new TextField();
				_traceField.multiline = true;
				_traceField.backgroundColor = 0xf9f9a0;
				_traceField.background = true;
				_traceField.autoSize = "left";
				_container.addChild(_traceField);
				_storedDebug = new Array();
				_intDbg = true;
			};
		};
		
		private static function setPosition():Point
		{
			
			var retPoint:Point;
			
			if (!_intDbg){initDebug()};
			
			if (!_container.stage)
			{
				return new Point(); 
			};
			
			switch (position)
			{
				case ALIGN_BOTTOM:
					retPoint = new Point(_container.stage.stageWidth/2 - _traceField.width /2, _container.stage.stageHeight - _traceField.height - _borderSpacing);
				break;
				
				case ALIGN_BOTTOMLEFT:
					retPoint = new Point(0 + _borderSpacing, _container.stage.stageHeight - _traceField.height - _borderSpacing);
				break;
				
				case ALIGN_BOTTOMRIGHT:
					retPoint = new Point(_container.stage.stageWidth - _traceField.width - _borderSpacing, _container.stage.stageHeight - _traceField.height - _borderSpacing);
				break;
				
				case ALIGN_LEFT:
					retPoint = new Point( 0 + _borderSpacing, _container.stage.stageHeight / 2 - _traceField.height /2);
				break;
				
				case ALIGN_RIGHT:
					retPoint = new Point( _container.stage.stageWidth - _traceField.width - _borderSpacing , _container.stage.stageHeight / 2 - _traceField.height /2);
				break;
				
				case ALIGN_TOP:
					retPoint = new Point( _container.stage.stageWidth/2 - _traceField.width /2, 0 + _borderSpacing);
				break;
				
				case ALIGN_TOPLEFT:
					retPoint = new Point( 0 + _borderSpacing, 0 + _borderSpacing);
				break;
				
				case ALIGN_TOPRIGHT:
					retPoint = new Point( _container.stage.stageWidth - _traceField.width - _borderSpacing, 0 + _borderSpacing);
				break;
				
			};
			
			return retPoint as Point;
			
		};
		
		private static var _storedDebug:Array;
		
		public static function debug(... args):void
		{
			
			setPosition();
			
			if (checkContainer())
			{
				
				if (!_intDbg){initDebug()};
				
				if (locked){return};
				
				var intArray:Array = new Array(args);
				
				_traceField.defaultTextFormat = new TextFormat(null,null,_textColor,null,null,null,null,null,_textAlign);
				
				if (_useHTML)
				{
					_traceField.htmlText = "";_textBreak = "<br/>";
				}
				else
				{
					_traceField.text = "";_textBreak = "\n";
				};
				
				addtext("====== Visual Tracer ======"+_textBreak);
				
				if (_header.length > 0)
				{
					addtext("[OUTPUT]"+_textBreak);
					addtext(_header);
				};
				
				if (intArray.length == 0)
				{
					intArray = _storedDebug;
				}
				else
				{
					
					addtext("------------- Debug -------------" + _textBreak);
					
					if (args.length > 0)
					{
						trace.apply(null, args);
						_dbgStr += (String(intArray.join(" ")) + _textBreak);
						_log += args + "\n";
					};
					
					var workArray:Array = _dbgStr.split(_textBreak);
					if (workArray.length > (_numLines+1)) { workArray.splice(0,1); _dbgStr = workArray.join(_textBreak); };
					
					if (intArray.length > 0)
					{
						_storedDebug = workArray;
					};
					
					addtext(_dbgStr);
					
					_traceField.mouseEnabled = _selectable;
					_traceField.selectable = _selectable;
					
				};
				
				_traceField.x = setPosition().x;
				_traceField.y = setPosition().y;
				_traceField.visible = _visible;
				_container.addChild(_traceField);
				return;
				
			};
		};
		
		private static function addtext(value:String):void
		{
			
			if (_useHTML)
			{
				_traceField.htmlText += value;
			}
			else
			{
				_traceField.appendText(value);
			};
			
		};
		
		public static function clear():void
		{
			_dbgStr = "";
			
			if (checkContainer() && _intDbg)
			{
				debug();
			};
		};
		
		private static function debugHeader(... args):void
		{
			if (checkContainer())
			{
				if (!_intDbg){initDebug()};
				if (locked){return;};
				
				var intArgs:Array = new Array(args);
				
				if (args.length > 0)
				{
					trace.apply(null, args);
					_header += (String(intArgs.join(" ")) + _textBreak);
				};
				
				debug();

			};
		};

		
		private static function clearHeader(numLines:Number = 0):void
		{
			
			if (checkContainer())
			{
				if (!_intDbg){initDebug()};
				if (locked){return;};
				
				if (numLines == 0)
				{
					_header = "";
				}
				else
				{
					_header = _header.split("\n").slice(numLines).join("\n");
				};
				
			};
			
			if (checkContainer() && _intDbg)
			{
				debug();
			};
			
		};
		
		
	};
};